package com.gitee.easyopen;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.easyopen.bean.ApiDefinition;
import com.gitee.easyopen.bean.DefinitionHolder;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.message.Errors;
import com.gitee.easyopen.util.ApiUtil;

/**
 * 处理客户端请求分发
 * 
 * @author tanghc
 *
 */
public class ApiInvoker {

    private static final Logger logger = LoggerFactory.getLogger(ApiInvoker.class);

    private static final String UTF8 = "UTF-8";
    private static final String FORMAT_JSON = "json";
    private static final String FORMAT_XML = "xml";

    private ApiConfig apiConfig;
    private Validator validator;
    private ResultCreator resultCreator;

    private ResultSerializer jsonFormatter;
    private ResultSerializer xmlFormatter;

    public ApiInvoker(ApiConfig apiConfig) {
        super();
        this.apiConfig = apiConfig;
        this.validator = apiConfig.getValidator();
        this.resultCreator = apiConfig.getResultCreator();

        this.jsonFormatter = apiConfig.getJsonResultSerializer();
        this.xmlFormatter = apiConfig.getXmlResultSerializer();
    }

    /**
     * 调用接口方法
     * 
     * @param request
     * @param paramClass
     *            接口参数,默认ApiParam.可实现Param自定义
     * @return
     * @throws IOException
     * @throws Throwable
     */
    public void invoke(HttpServletRequest request, HttpServletResponse response) {
        ApiContext.setRequest(request);

        Result result = null;
        ResultCreator resultCreator = this.getResultCreator();

        ApiParam param = this.buildApiParam(request);
        try {
            logger.info("收到客户端请求,ip={},参数={}", ApiUtil.getClientIP(request), param.toJSONString());

            Object returnObj = this.doInvoke(param);
            // 执行成功返回json
            result = resultCreator.createResult(returnObj);
        } catch (ApiException e) {
            result = resultCreator.createErrorResult(e.getError().getCode(), e.getError().getMsg(), e.getData());
        } catch (Throwable e) {
            result = this.caugthException(e);
        }

        this.responseResult(response, result, param.fatchFormat());
    }

    public Result caugthException(Throwable e) {
        return resultCreator.createErrorResult(Errors.SYS_ERROR.getCode(), e.getMessage(), null);
    }
    
    /**
     * 写数据到客户端
     * 
     * @param response
     * @param result 结果
     */
    public void responseResult(HttpServletResponse response, Result result) {
        this.responseResult(response, result, FORMAT_JSON);
    }

    /**
     * 写数据到客户端
     * @param response
     * @param result 结果
     * @param format 返回类型，json,xml之一
     */
    public void responseResult(HttpServletResponse response, Result result, String format) {
        String text = this.serializeResult(result, format);
        ApiUtil.writeText(response, text);
    }

    private String serializeResult(Result result, String format) {
        ResultSerializer resultSerializer = this.getResultSerializer(format);

        return resultSerializer.serialize(result);
    }

    private ResultSerializer getResultSerializer(String format) {
        if (FORMAT_XML.equalsIgnoreCase(format)) {
            return this.xmlFormatter;
        } else {
            return this.jsonFormatter;
        }
    }

    protected ApiParam buildApiParam(HttpServletRequest request) {
        String requestJson;
        try {
            requestJson = IOUtils.toString(request.getInputStream(), UTF8);
        } catch (IOException e) {
            throw new ApiException(e);
        }
        if (StringUtils.isEmpty(requestJson)) {
            throw Errors.SERVICE_INVALID.getException();
        }
        JSONObject jsonObject = JSONObject.parseObject(requestJson);

        return new ApiParam(jsonObject);
    }

    protected Object doInvoke(ApiParam param) throws Throwable {
        ApiDefinition definition = DefinitionHolder.getByParam(param);
        if (definition == null) {
            throw Errors.NO_API.getException(param.fatchName(), param.fatchVersion());
        }
        param.setIgnoreSign(definition.isIgnoreSign());
        param.setIgnoreValidate(definition.isIgnoreValidate());
        
        this.getValidator().validate(param);

        String json = param.fatchData();
        json = URLDecoder.decode(json, UTF8);

        Class<?> arguClass = definition.getMethodArguClass();

        Object methodArgu = null;

        if (arguClass != null) {
            methodArgu = JSON.parseObject(json, arguClass);
        }

        try {
            // 调用method方法
            if (methodArgu == null) {
                return definition.getMethod().invoke(definition.getHandler());
            } else {
                return definition.getMethod().invoke(definition.getHandler(), methodArgu);
            }
        } catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        } catch (Exception e) {
            throw e;
        }
    }

    public ApiConfig getApiConfig() {
        return apiConfig;
    }

    public void setApiConfig(ApiConfig apiConfig) {
        this.apiConfig = apiConfig;
    }

    public Validator getValidator() {
        return validator;
    }

    public void setValidator(Validator validator) {
        this.validator = validator;
    }

    public ResultCreator getResultCreator() {
        return resultCreator;
    }

    public void setResultCreator(ResultCreator resultCreator) {
        this.resultCreator = resultCreator;
    }

}
